// Copyright 2016 Google, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
////////////////////////////////////////////////////////////////////////////////

package com.firebase.jobdispatcher;

import android.support.annotation.IntDef;
import android.support.annotation.NonNull;
import com.firebase.jobdispatcher.RetryStrategy.RetryPolicy;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

/**
 * The FirebaseJobDispatcher provides a driver-agnostic API for scheduling and cancelling Jobs.
 *
 * @see #FirebaseJobDispatcher(Driver)
 * @see Driver
 * @see JobParameters
 * @deprecated Firebase Job Dispatcher is deprecated. Apps should migrate to WorkManager before Apr
 *     7, 2020. Please see FJD's README.md file for more information.
 */
@Deprecated
public final class FirebaseJobDispatcher {
  /** Indicates the schedule request seems to have been successful. */
  public static final int SCHEDULE_RESULT_SUCCESS = 0;

  /** Indicates the schedule request encountered an unknown error. */
  public static final int SCHEDULE_RESULT_UNKNOWN_ERROR = 1;

  /** Indicates the schedule request failed because the driver was unavailable. */
  public static final int SCHEDULE_RESULT_NO_DRIVER_AVAILABLE = 2;

  /** Indicates the schedule request failed because the Trigger was unsupported. */
  public static final int SCHEDULE_RESULT_UNSUPPORTED_TRIGGER = 3;

  /**
   * Indicates the schedule request failed because the service is not exposed or configured
   * correctly.
   */
  public static final int SCHEDULE_RESULT_BAD_SERVICE = 4;

  /** Indicates the cancel request seems to have been successful. */
  public static final int CANCEL_RESULT_SUCCESS = 0;
  /** Indicates the cancel request encountered an unknown error. */
  public static final int CANCEL_RESULT_UNKNOWN_ERROR = 1;
  /** Indicates the cancel request failed because the driver was unavailable. */
  public static final int CANCEL_RESULT_NO_DRIVER_AVAILABLE = 2;
  /** The backing Driver for this instance. */
  private final Driver driver;
  /** The ValidationEnforcer configured for the current Driver. */
  private final ValidationEnforcer validator;
  /**
   * Single instance of a RetryStrategy.Builder, configured with the current driver's validation
   * settings. We can do this because the RetryStrategy.Builder is stateless.
   */
  private final RetryStrategy.Builder retryStrategyBuilder;

  /** Instantiates a new FirebaseJobDispatcher using the provided Driver. */
  public FirebaseJobDispatcher(@NonNull Driver driver) {
    this.driver = driver;
    validator = new ValidationEnforcer(driver.getValidator());
    retryStrategyBuilder = new RetryStrategy.Builder(validator);
  }

  /**
   * Attempts to schedule the provided Job.
   *
   * <p>Returns one of the SCHEDULE_RESULT_ constants.
   */
  @ScheduleResult
  public int schedule(@NonNull Job job) {
    if (!driver.isAvailable()) {
      return SCHEDULE_RESULT_NO_DRIVER_AVAILABLE;
    }

    return driver.schedule(job);
  }

  /**
   * Attempts to cancel the Job that matches the provided tag and endpoint.
   *
   * <p>Returns one of the CANCEL_RESULT_ constants.
   */
  @CancelResult
  public int cancel(@NonNull String tag) {
    if (!driver.isAvailable()) {
      return CANCEL_RESULT_NO_DRIVER_AVAILABLE;
    }

    return driver.cancel(tag);
  }

  /**
   * Attempts to cancel all Jobs registered for this package.
   *
   * <p>Returns one of the CANCEL_RESULT_ constants.
   */
  @CancelResult
  public int cancelAll() {
    if (!driver.isAvailable()) {
      return CANCEL_RESULT_NO_DRIVER_AVAILABLE;
    }

    return driver.cancelAll();
  }

  /**
   * Attempts to schedule the provided Job, throwing an exception if it fails.
   *
   * @throws ScheduleFailedException
   */
  public void mustSchedule(@NonNull Job job) {
    if (schedule(job) != SCHEDULE_RESULT_SUCCESS) {
      throw new ScheduleFailedException();
    }
  }

  /** Returns a ValidationEnforcer configured for the current Driver. */
  public ValidationEnforcer getValidator() {
    return validator;
  }

  /** Creates a new Job.Builder, configured with the current driver's validation settings. */
  @NonNull
  public Job.Builder newJobBuilder() {
    return new Job.Builder(validator);
  }

  /**
   * Creates a new RetryStrategy from the provided parameters, validated with the current driver's
   * {@link JobValidator}.
   *
   * @param policy the backoff policy to use. One of the {@link RetryPolicy} constants.
   * @param initialBackoff the initial backoff, in seconds.
   * @param maximumBackoff the maximum backoff, in seconds.
   * @throws ValidationEnforcer.ValidationException
   * @see RetryStrategy
   */
  public RetryStrategy newRetryStrategy(
      @RetryPolicy int policy, int initialBackoff, int maximumBackoff) {

    return retryStrategyBuilder.build(policy, initialBackoff, maximumBackoff);
  }

  /** Results that can legally be returned from {@link #schedule(Job)} calls. */
  @IntDef({
    SCHEDULE_RESULT_SUCCESS,
    SCHEDULE_RESULT_UNKNOWN_ERROR,
    SCHEDULE_RESULT_NO_DRIVER_AVAILABLE,
    SCHEDULE_RESULT_UNSUPPORTED_TRIGGER,
    SCHEDULE_RESULT_BAD_SERVICE,
  })
  @Retention(RetentionPolicy.SOURCE)
  public @interface ScheduleResult {}

  /**
   * Results that can legally be returned from {@link #cancel(String)} or {@link #cancelAll()}
   * calls.
   */
  @IntDef({
    CANCEL_RESULT_SUCCESS,
    CANCEL_RESULT_UNKNOWN_ERROR,
    CANCEL_RESULT_NO_DRIVER_AVAILABLE,
  })
  @Retention(RetentionPolicy.SOURCE)
  public @interface CancelResult {}

  /**
   * Thrown when a {@link FirebaseJobDispatcher#schedule(com.firebase.jobdispatcher.Job)} call
   * fails.
   */
  public static final class ScheduleFailedException extends RuntimeException {}
}
